/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <os_thread.h>

#include "iot_boardbutton_xradio.h"
#include "gpio_if.h"
#include "cmsis_os2.h"
#include "ohos_init.h"

#define KEY_OK      1
#define TASK_SIZE   1024
#define LONG_PRESS_TIME 2000

#define USER_DEF    0
#define USER_XR     1

#define CS_SELECT   USER_XR

#if (CS_SELECT == USER_XR)
#define KEY_BUTTON_IO   11
#else
#define KEY_BUTTON_IO   0
#endif

#define SLEEP_10MS              10000
#define SLEEP_100MS             100000
#define MINISECONDS_IN_SECOND   1000

#define BTTN_DBG(fmt, arg...)   printf("[%s|%d][BUTTON_DEBUG]" fmt, __func__, __LINE__, ##arg)
#define BTTN_ERR(fmt, arg...)   printf("[%s|%d][BUTTON_ERROR]" fmt, __func__, __LINE__, ##arg)

static ButtonPressedCallback g_ButtonPressedCallback = NULL;
static unsigned char g_button_state = BUTTON_INVALID;
static OS_Thread_t g_button_thread;

static unsigned long GetMillionSecond(void)
{
    struct  timeval tv;

    gettimeofday(&tv, NULL);

    return tv.tv_sec * MINISECONDS_IN_SECOND + tv.tv_usec / MINISECONDS_IN_SECOND;        // 1000 is millionsecond
}

static int KeyIrqCallback(int gpio, void *arg)
{
    BTTN_DBG("KeyIrqCallback! gpio = %d \n", gpio);
    return 0;
}

static int g_threadRunning = 0;

static void ButtonEventThread(void *arg)
{
    ButtonEvent *event = (ButtonEvent *)arg;
    if (g_ButtonPressedCallback != NULL) {
        g_ButtonPressedCallback(event);
    }
    g_threadRunning = 0;
    BTTN_DBG("g_threadRunning = %d \n", g_threadRunning);
}

static void ButtonEventHandle(ButtonEvent *event)
{
    BTTN_DBG("g_threadRunning = %d \n", g_threadRunning);
    if (g_threadRunning == 0) {
        const int priority = 25;
        const int stack_size = (1024 * 2);
        osThreadAttr_t attr;
        attr.attr_bits = 0U;
        attr.cb_mem = NULL;
        attr.cb_size = 0U;
        attr.stack_mem = NULL;
        attr.stack_size = stack_size;
        attr.priority = priority;
        attr.name = "ButtonEventThread";
        g_threadRunning = 1;
        if (osThreadNew((osThreadFunc_t)ButtonEventThread, event, (const osThreadAttr_t *)&attr) == NULL) {
            BTTN_ERR("create ButtonEventThread failed! \n");
            g_threadRunning = 0;
            return;
        }
    }
}

static void ButtonProcess(void *args)
{
    unsigned long pressTime = 0;
    ButtonEvent event;

    GpioSetIrq(KEY_BUTTON_IO, GPIO_IRQ_TRIGGER_FALLING, KeyIrqCallback, NULL);
    GpioEnableIrq(KEY_BUTTON_IO);
    while (1) {
        int value = 0;
        if (GpioRead(KEY_BUTTON_IO, &value) < 0) {
            BTTN_ERR("read io(%d) failed! \n", KEY_BUTTON_IO);
            sleep(1);
            continue;
        }

        if (value == 0) {   // button pressed!
            if (g_button_state == BUTTON_PRESS) {
                if (pressTime == 0) {
                    pressTime = GetMillionSecond();
                }
                if ((GetMillionSecond() - pressTime) >= LONG_PRESS_TIME) {
                    event.button_id = KEY_BUTTON_IO;
                    event.value = BUTTON_LONG_PRESS;
                    g_button_state = BUTTON_LONG_PRESS;
                    ButtonEventHandle(&event);
                }
            } else {
                if (g_button_state != BUTTON_LONG_PRESS) {
                    g_button_state = BUTTON_PRESS;
                }
            }
        } else {    // button release or no button pressed!
            if (g_button_state == BUTTON_PRESS) {
                event.button_id = KEY_BUTTON_IO;
                event.value = BUTTON_PRESS;
                ButtonEventHandle(&event);
            }
            if (g_button_state != BUTTON_RELEASE) {
                pressTime = 0;
                g_button_state = BUTTON_RELEASE;
            }
        }
        usleep(SLEEP_10MS);
    }
}

int Board_ButtonInit(ButtonPressedCallback callback)
{
    g_ButtonPressedCallback = callback;
    if (OS_ThreadCreate(&g_button_thread, "ButtonProcess", ButtonProcess,
        NULL, OS_THREAD_PRIO_APP, TASK_SIZE) != OS_OK) {
        BTTN_ERR("OS_ThreadCreate failed! \n");
        return -1;
    }
    return 0;
}

int Board_IsButtonPressed(void)
{
    int tmCount = 20;
    int result = 1;
    while (tmCount-- > 0) {
        int value = 0;
        if (GpioRead(KEY_BUTTON_IO, &value) < 0) {
            BTTN_ERR("read io(%d) failed! \n", KEY_BUTTON_IO);
            result = 0;
            break;
        }
        if (value == 1) {
            BTTN_DBG("key have not pressed!\n");
            result = 0;
            break;
        }
        usleep(SLEEP_100MS);
    }

    return result;
}
